'''
窗口显示图片，画矩形框，可鼠标调整矩形框
交互式改参数界面 步骤1
    选择相机盖中心点
    选择相机盖距离阈值
    选择电线高度阈值
'''
import math
import random
import sys
import traceback

import cv2
import numpy
import numpy as np
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
from PyQt5.QtCore import Qt, QRectF, QPointF, QLineF
from PyQt5.QtGui import QPainter, QPixmap, QImage, QPen, QColor, QFont, QCursor

import yaml

from log import saved_algo_param_dict #

def array2pix(image, ismask=False):
    if len(image.shape) == 2:
        image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGRA)

    if image.shape[2] == 1:
        image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGRA)

    if image.shape[2] == 3:
        #image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
    #show1 = cv2.resize(image, (640, 480))
    if ismask:
        #image[:,:,3] = image[:,:,2]
        pass

    show1 = image
    #QtImg = QImage(show1.data, show1.shape[1], show1.shape[0], QImage.Format_BGR888)
    QtImg = QImage(
        show1.data, show1.shape[1], show1.shape[0], QImage.Format_ARGB32)
    pixmap = QPixmap(QtImg)
    return pixmap

def read_yaml(file_path):
    with open(file_path, 'r') as file:
        config = yaml.safe_load(file)
    return config


def write_yaml(data, file_path):
    with open(file_path, 'w') as file:
        yaml.safe_dump(data, file)
class ImageWithResizableBox(QWidget):
    def __init__(self, imagePath, default_threshold_list=[100, 3280, 1920, 45, 1000]):
        '''

        :param imagePath:
        :param default_threshold_list: 电线高度 px; 相机中心点 px px，距离 px，角度 degree；
        '''
        super().__init__()
        # 设置图片
        self.imagePath = imagePath
        if isinstance(imagePath, numpy.ndarray):
            self.orig_pixmap = array2pix(imagePath)
        else:
            self.orig_pixmap = QPixmap(imagePath)
        self.chk_imgorig_x1, self.chk_imgorig_y1 = 0, 0 # 图片左上角 相对窗口坐标系
        self.scale = 0.2
        self.orig_scale = self.scale
        self.orig_img_height, self.orig_img_width = self.orig_pixmap.height(), self.orig_pixmap.width()
        self.pixmap = self.orig_pixmap.scaled(int(self.orig_pixmap.width()*self.scale), int(self.orig_pixmap.height()*self.scale), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self.setWindowTitle('修改步骤1阈值')
        self.resize(self.pixmap.width(), self.pixmap.height())  # 调整窗口大小以适应图片

        # 载入初始参数
        try:
            self.param_path = 'config/param.yaml'
            param_dict = read_yaml(self.param_path)
            if 1 or param_dict and param_dict.get('step1'):
                self.threshold_list = param_dict['step1'] # 高度h; 相机盖中心点 （x,y）; 距离（d）;
        except:
            try:
                self.param_path = r'D:\code\git\zxc\fuba_\project_format_fubatianxian\config\param.yaml'
                param_dict = read_yaml(self.param_path)
                if 1 or param_dict and param_dict.get('step1'):
                    self.threshold_list = param_dict['step1']  #
            except:
                self.threshold_list = default_threshold_list # 要设置的阈值

        # 设置矩形
        dianxian_h_param = self.threshold_list[0]
        xiangjigai_x_param, xiangjigai_y_param, xiangjigai_u_param, xiangjigai_dis_param = self.threshold_list[1:]
        # 32 19
        ret_width = xiangjigai_dis_param * math.cos(math.radians(xiangjigai_u_param))
        ret_height = xiangjigai_dis_param * math.sin(math.radians(xiangjigai_u_param))
        print(f'ret_width {ret_width} ret_height {ret_height}')
        self.rect_list = [QRectF(int(4530*self.scale), int((2320-dianxian_h_param)*self.scale), 0, int(dianxian_h_param * self.scale)),# x1y1wh；电线高度参数
                          QRectF(int(xiangjigai_x_param*self.scale), int((xiangjigai_y_param-ret_height)*self.scale), ret_width*self.scale, ret_height*self.scale), # 相机盖参数
                          ]
        self.handle_size = 10
        self.dragging_listrect_listconer = [[False, False, False, False], [False, False, False, False]]  # [[rect1],[rect2]]

    def paintEvent(self, event):
        try:
            print('paintEvent')
            # self.resize(self.pixmap.width(), self.pixmap.height())  # 调整窗口大小以适应图片
            painter = QPainter(self)
            painter.drawPixmap(self.chk_imgorig_x1, self.chk_imgorig_y1, self.pixmap) # 画图 x1y1
            # painter.drawPixmap(-100, -300, self.pixmap)  # 画图 x1y1

            painter.setRenderHint(QPainter.Antialiasing)
            # pen = QPen(QColor(255, 0, 0), 2)
            # painter.setPen(pen)


            # 绘制矩形，矩形边角方框
            for ind, rect in enumerate(self.rect_list):
                if ind == 0: # 电线阈值矩形框 红色
                    painter.setPen(QPen(QColor(255, 0, 0), 2))
                    painter.drawRect(rect)  # 画矩形
                    for corner in [(rect.topLeft(), 1), (rect.topRight(), 2),
                                   (rect.bottomRight(), 3), (rect.bottomLeft(), 4)]:
                        painter.drawRect(corner[0].x() - self.handle_size // 2,
                                         corner[0].y() - self.handle_size // 2,
                                         self.handle_size, self.handle_size)

                elif ind == 1: # 相机盖原点，距离，角度
                    # rect.setRect(100,200,500,500)
                    painter.setPen(QPen(QColor(0, 0, 255), 2))
                    painter.drawRect(rect)  # 画矩形
                    painter.drawLine(rect.bottomLeft().x(), rect.bottomLeft().y(), rect.topRight().x(), rect.topRight().y()) # 对角线

                    for corner in [(rect.topLeft(), 1), (rect.topRight(), 2),
                                   (rect.bottomRight(), 3), (rect.bottomLeft(), 4)]:
                        if corner[1] == 4:
                            painter.setPen(QPen(QColor(255, 0, 0), 2)) # 红色
                        else:
                            painter.setPen(QPen(QColor(0, 0, 255), 2))
                        painter.drawRect(corner[0].x() - self.handle_size // 2,
                                         corner[0].y() - self.handle_size // 2,
                                         self.handle_size, self.handle_size) # 矩形边角小方框

            # 设置字体
            font = QFont('Arial', 18)
            painter.setFont(font)

            painter.setPen(QPen(QColor(0, 0, 255), 2))
            painter.drawText(40, 560, f'相机盖原点 x y:{self.threshold_list[1]} {self.threshold_list[2]}')
            painter.drawText(40, 600, f'相机盖角度:{self.threshold_list[3]} 度')
            painter.drawText(40, 640, f'相机盖距离:{self.threshold_list[4]}')
            painter.setPen(QPen(QColor(255, 0, 0), 2))
            painter.drawText(40, 680, f'电线高度阈值:{self.threshold_list[0]} ')  # 文字左下角的位置 (x, y)
        except:
            print(traceback.format_exc())

    def mouseDoubleClickEvent(self, event):
        self.update()

    def mousePressEvent(self, event): # 鼠标按下
        try:
            print('xxxx')
            if event.button() == Qt.LeftButton:
                # 选择选中的矩形角点
                for rect_ind, rect in enumerate(self.rect_list):
                    self.rect = rect
                    for corner, handle_index in [(self.rect.topLeft(), 1), (self.rect.topRight(), 2),
                                                 (self.rect.bottomRight(), 3), (self.rect.bottomLeft(), 4)]:

                        print(event.globalPos()) # 全局坐标
                        print(event.pos())  # 局部坐标
                        print((event.globalPos() - self.mapToGlobal(corner.toPoint())).manhattanLength())
                        if (event.globalPos() - self.mapToGlobal(corner.toPoint())).manhattanLength() <= self.handle_size / 2:

                            self.dragging_listrect_listconer[rect_ind][handle_index-1] = True  # 是否选中
                            # 改变鼠标形状为“选中”或手势指示的光标
                            hand_cursor = QCursor(Qt.PointingHandCursor)
                            self.setCursor(hand_cursor)
                            # break
                        else:
                            self.dragging_listrect_listconer[rect_ind][handle_index - 1] = False
        except:
            print(traceback.format_exc())


    def mouseMoveEvent(self, event): # 鼠标移动
        try:
            print('mouseMoveEvent')
            # 交互式修改阈值
            fun_list = [[self.rect_list[0].setTopLeft, self.rect_list[0].setTopRight, self.rect_list[0].setBottomRight, self.rect_list[0].setBottomLeft],
                        [self.rect_list[1].setTopLeft, self.rect_list[1].setTopRight, self.rect_list[1].setBottomRight, self.rect_list[1].setBottomLeft]
                        ]
            global_pos = event.globalPos()
            for rect_ind, dragging_listconer in enumerate(self.dragging_listrect_listconer): # 每个矩形框
                if rect_ind == 0: # 电线高度项； 上下移动
                    hei = self.rect_list[rect_ind].height()
                    if hei == 0:
                        print(f'矩形高度为0')
                        dragging_listconer[2] = False
                        dragging_listconer[3] = False
                    for coner_ind, is_dragging in enumerate(dragging_listconer):
                        if is_dragging:
                            fun_list[rect_ind][coner_ind](self.mapFromGlobal(global_pos)) # 这里移动矩形角点
                            self.threshold_list[0] = float(np.round(self.rect_list[rect_ind].height()/self.scale,2))  # 同步电线高度阈值
                elif rect_ind == 1: # 相机盖项
                    wid, heig = self.rect_list[rect_ind].width(), self.rect_list[rect_ind].height()
                    if wid == 0 or heig == 0:
                        print(f'矩形宽度,或高度为0 ')
                        selected_coner_int = random.randint(0,3)
                        for i in range(4):
                            if i != selected_coner_int:
                                dragging_listconer[i] = False
                    for coner_ind, is_dragging in enumerate(dragging_listconer):
                        if is_dragging:
                            fun_list[rect_ind][coner_ind](self.mapFromGlobal(global_pos)) # 这里移动矩形角点

                            # x1, y1 = self.rect_list[rect_ind].topLeft().x(), self.rect_list[rect_ind].topLeft().y()
                            # x2, y2 = self.rect_list[rect_ind].bottomRight().x(), self.rect_list[rect_ind].bottomRight().y()

                            self.threshold_list[1] = int(np.round((self.rect_list[rect_ind].bottomLeft().x()-self.chk_imgorig_x1)/self.scale, 2)) # 相机盖原点
                            self.threshold_list[2] = int(np.round((self.rect_list[rect_ind].bottomLeft().y() -self.chk_imgorig_y1) / self.scale, 2))  # 相机盖原点
                            self.threshold_list[3] = float(np.round(math.degrees(math.atan2(self.rect_list[rect_ind].height(), self.rect_list[rect_ind].width())),2)) # 相机盖角度阈值
                            self.threshold_list[4] = float(np.round(math.sqrt(self.rect_list[rect_ind].height()**2 + self.rect_list[rect_ind].width()**2)/self.scale,2)) # 对角线
                            #xiangjigai_x_param, xiangjigai_y_param, xiangjigai_u_param, xiangjigai_dis_param = self.threshold_list[1:]


                # self.paintText()

            self.update()  # 执行paintEvent()
        except:
            print(traceback.format_exc())

    def mouseReleaseEvent(self, event): # 鼠标释放
        try:
            print('mouseReleaseEvent')
            param_dict = read_yaml(self.param_path)
            if param_dict:
                param_dict['step1'] = self.threshold_list
                # param_dict['step1'] = [1,23]
                # write_yaml(param_dict, self.param_path)

                # write_yaml(param_dict['step1'], self.param_path+'_step1.yaml')
                saved_algo_param_dict['step1'] = self.threshold_list

            self.setCursor(QCursor(Qt.ArrowCursor)) # 默认箭头

            self.update()
        except:
            print(traceback.format_exc())

    def wheelEvent(self, event):
        try:
            print('wheelEvent')
            print(event.angleDelta().x())
            print(event.angleDelta().y()) # 120 -120
            print(event.globalPos())  # 全局坐标
            print(event.pos())  # 局部坐标

            # 1 以鼠标为原点 缩放图像
            pre_scale = self.scale
            step_len = 0.05*self.scale * event.angleDelta().y() / 120
            print(f'step_len {step_len}')
            # self.scale = self.scale*self.scale/self.orig_scale*event.angleDelta().y() / 120
            self.scale = self.scale + step_len  # 1%
            chk_shb_x, chk_shb_y = event.pos().x(), event.pos().y()  # 相对窗口坐标系 鼠标坐标

            tx_shb_x, tx_shb_y = (chk_shb_x-self.chk_imgorig_x1), (chk_shb_y-self.chk_imgorig_y1) # 计算图像左上角 相对窗口坐标系
            self.chk_imgorig_x1 = self.chk_imgorig_x1 - step_len * tx_shb_x/pre_scale
            self.chk_imgorig_y1 = self.chk_imgorig_y1 - step_len * tx_shb_y/pre_scale # 平移

            # 缩放图片
            self.pixmap = self.orig_pixmap.scaled(int(self.orig_img_width * self.scale), int(self.orig_img_height * self.scale),
                                             Qt.KeepAspectRatio, Qt.SmoothTransformation)
            # painter = QPainter(self)
            # painter.drawPixmap(0, 0, self.pixmap)  # 画图

            # # 2 缩放平移矩形框
            # 矩形左上角 相对窗口坐标系
            for qrect in self.rect_list:
                chk_rect_x1, chk_rect_y1 = qrect.topLeft().x(), qrect.topLeft().y()
                chk_rect_x1 = (chk_rect_x1 -chk_shb_x)/pre_scale*self.scale + chk_shb_x
                chk_rect_y1 = (chk_rect_y1 - chk_shb_y)/pre_scale * self.scale + chk_shb_y  # 平移

                # chk_rect_x1 = chk_rect_x1 - step_len * tx_shb_x / pre_scale
                # chk_rect_y1 = chk_rect_y1 - step_len * tx_shb_y / pre_scale
                w, h = qrect.width()/pre_scale * self.scale, qrect.height()/pre_scale*self.scale
                qrect.setRect(chk_rect_x1, chk_rect_y1, w, h)

            self.update() # paintEvent

        except:
            print(traceback.format_exc())

    def closeEvent(self, event):
        saved_algo_param_dict['step1'] = []

if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = ImageWithResizableBox(r"D:\data\save\fuba_tianxian\20240603\service1ng\step1_20240603_142024_077\img_1_20240603_142030_520_step1_NG_img.jpg")  # 替换为初始图片路径
    widget.resize(widget.pixmap.width(), widget.pixmap.height())  # 调整窗口大小以适应图片
    widget.show()
    sys.exit(app.exec_())
